﻿using Prism.Commands;
using Quartz;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ZYing.QPanel.ViewModels
{
    public class LogsViewModel : NavigationViewModelBase
    {
        public class LogItem
        {
            public LogItem(StatusFiredEventArgs e)
            {
                Job = e.Context.JobDetail.Key;
                FireTime = e.FireTime;
                Status = e.Status;
            }
            public JobKey Job { get; }
            public DateTimeOffset FireTime { get; }
            public string Status { get; }
        }
        private static ConcurrentQueue<LogItem> _logs = new ConcurrentQueue<LogItem>();
        public static void Start()
        {
            Trace.WriteLine(nameof(LogsViewModel));
        }
        private const int MAX_LOG_COUNT = 500;
        public IScheduler Scheduler { get; }
        public LogsViewModel(IScheduler scheduler)
        {
            Scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler));
            this.ReloadCommand = new DelegateCommand(DoReload);
            DoReload();
            Run(this.CancellationTokenSource.Token);
        }
        static LogsViewModel()
        {
            JobHelper.StatusFired += JobHelper_StatusFired;
        }

        private static void JobHelper_StatusFired(object sender, StatusFiredEventArgs e)
        {
            _logs.Enqueue(new LogItem(e));
            while (_logs.Count > MAX_LOG_COUNT && _logs.TryDequeue(out _)) ;
        }

        public ObservableCollection<string> Groups { get; } = new ObservableCollection<string>();
        public ObservableCollection<LogItem> Logs { get; } = new ObservableCollection<LogItem>();

        private string _keyword;

        public string Keyword
        {
            get { return _keyword; }
            set { if (SetProperty(ref _keyword, value)) this.DoReload(); }
        }
        private string _group;

        public string Group
        {
            get { return _group; }
            set { if (SetProperty(ref _group, value)) this.DoReload(); }
        }
        private async void DoReload()
        {
            await Reload();
        }
        public DelegateCommand ReloadCommand { get; }
        public async Task Reload()
        {
            var groups = await Scheduler.GetJobGroupNames();
            foreach (var name in groups)
            {
                if (Groups.Contains(name)) continue;
                Groups.Add(name);
            }
            var tmp = new List<LogItem>(_logs.Count);
            if (string.IsNullOrWhiteSpace(this.Group) && string.IsNullOrWhiteSpace(Keyword))
            {
                tmp.AddRange(_logs);
            }
            else
            {
                foreach (var item in _logs)
                {
                    if (!string.IsNullOrEmpty(this.Group))
                    {
                        if (item.Job.Group != this.Group) continue;
                    }
                    if (!string.IsNullOrWhiteSpace(this.Keyword))
                    {
                        var str = item.Job.ToString();
                        if (
                            str.IndexOf(Keyword, StringComparison.OrdinalIgnoreCase) < 0
                            && item.Status.IndexOf(Keyword, StringComparison.OrdinalIgnoreCase) < 0
                            ) continue;
                    }
                    tmp.Add(item);
                }
            }
            var set = new HashSet<LogItem>(Logs);
            foreach (var job in tmp)
            {
                if (set.Contains(job))
                {
                    set.Remove(job);
                }
                else
                {
                    Logs.Insert(0, job);
                }
            }

            if (set.Count > 0)
            {
                foreach (var job in set)
                {
                    this.Logs.Remove(job);
                }
            }
        }

        private async void Run(CancellationToken cancellationToken)
        {
            while (true)
            {
                if (cancellationToken.IsCancellationRequested) break;
                if (this.IsActive)
                {
                    try
                    {
                        await this.Reload();
                    }
                    catch (Exception e)
                    {
                        Trace.WriteLine(e);
                    }
                }
                await Task.Delay(1000);
            }
        }
    }
}
